#include "Map.h"

Map::Map()
	: mpSprites(nullptr) 
	, mpTiles(nullptr)
	, mNumSprites(0)
	, mTileSize(0)
	, mColumns (0)
	, mRows(0)
{}

Map::~Map()
{
}

void Map::Load(const char* pLevel, const char* pTexturePack)
{
	Unload(); //in case load is already pointing at something
	
	//LEVEL DIMENSIONS AND LAYOUT
	if(!LoadLevel(pLevel))
	{
		Unload();
		return; //if something goes wrong, unload and return
	}

	//LEVEL TEXTURES
	if(!LoadTexturePack(pTexturePack))
	{
		Unload();
		return; //if something goes wrong, unload and return
	}
}

void Map::Unload()
{
	for(int i=0; i<mNumSprites; ++i)
	{
		mpSprites[i].Unload();
	}
	delete[] mpSprites;
	mpSprites = nullptr;

	delete[] mpTiles;
	mpTiles = nullptr;

	mNumSprites = 0;
	mTileSize = 0;
	mColumns = 0;
	mRows = 0;
}

void Map::Update(float deltaTime)
{
}

void Map::Render(const SVector2& viewOffset)
{
	for(int y=0; y<mRows; ++y) //y is rows because we are going through each row
	{
		for(int x=0; x<mColumns; ++x) //and vice versa
		{
			const int index = GetIndex(x, y);
			SVector2 pos((float) x * mTileSize, (float) y *mTileSize);
			SVector2 renderPosition = pos - viewOffset;
			mpSprites[mpTiles[index]].SetPosition(renderPosition); 
			mpSprites[mpTiles[index]].Render(); //double index 
		}
	}
}

bool Map::TestCollision(const SLineSegment& segment, SRect& rect) const
{
	//given a line segment, which cell are we talking about?

	//test boundary - segment uses from instead of min etc.
	//all of these or statements are conditions which mean the segment is outside the boundary
	if(segment.from.x < 0.0f || segment.from.x >= GetWidth() ||
		segment.from.y < 0.0f || segment.from.y >= GetHeight() ||
		segment.to.x < 0.0f || segment.to.x >= GetWidth() ||
		segment.to.y < 0.0f || segment.to.y >= GetHeight())
	{
		return false;
	}

	//if we pass this check, our location is valid and within the map

	const int minX = (int)(segment.from.x / mTileSize);
	const int minY = (int)(segment.from.y / mTileSize);
	const int maxX = (int)(segment.to.x / mTileSize);
	const int maxY = (int)(segment.to.y / mTileSize);

	//set rectangle to default
	rect = SRect();

	bool collision = false;

	//compute indices for all of the cells within the line segment
	for(int y= minY; y<= maxY; ++y)
	{
		for(int x= minX; x<= maxX; ++x)
		{
			if(IsBlocked(x,y))
			{
				//keep track of cell we are hitting and add to bounding box
				rect += GetBoundingBox(x,y);
				collision = true; //collision occured
			}
		}
	}

	return collision;
}

bool Map::LoadLevel(const char* pLevel)
{
	FILE* pFile = nullptr; //***TALK ABOUT POINTERS AND REFERENCES
	fopen_s(&pFile, pLevel, "r");
	if(pFile == nullptr)
	{
		return false; //IF ANYTHING GOES WRONG RETURN FALSE***
	} //if pFile is still null after loading, unload and bail
	
	//file to be scanned, string space integer format
	//%*s reads a string and then discards it, we only care about the int
	fscanf_s(pFile, "%*s %d", &mTileSize); 
	fscanf_s(pFile, "%*s %d", &mColumns);
	fscanf_s(pFile, "%*s %d", &mRows);
	mpTiles = new int [mColumns * mRows];

	//need to advance text file cursor by consuming newline (return) character
	fgetc(pFile);

	for(int y=0; y<mRows; ++y) //y is rows because we are going through each row
	{
		for(int x=0; x<mColumns; ++x) //and vice versa
		{
			//read one character at a time, convert from char 0 to int 0
			//by subtracting ascii value of '0'
			const int index = GetIndex(x, y);
			mpTiles[index] = fgetc(pFile) - '0';
		}
		fgetc(pFile); //to consume new line character
	}

	fclose(pFile);
	return true; //**SAFELY REACH THE END OF LOAD LEVEL
}

bool Map::LoadTexturePack(const char* pTexturePack)
{
	FILE* pFile = nullptr;
	fopen_s(&pFile, pTexturePack, "r");
	if(pFile == nullptr)
	{
		return false;
	}

	fscanf(pFile, "%*s %d", &mNumSprites);
	mpSprites = new SGE_Sprite[mNumSprites];

	char buffer[256]; //buffer to stop from memory stomping
	for(int i=0; i<mNumSprites; ++i)
	{
		fscanf_s(pFile, "%s", buffer, 256);
		mpSprites[i].Load(buffer);
	}

	fclose(pFile);
	return true; 
}

int Map::GetIndex(int x, int y) const
{
	//convert 2D index to 1D
	return x + (y *mColumns);
}

bool Map::IsBlocked(int x, int y) const
{
	const int index = GetIndex(x,y);
	int type = mpTiles[index];

	//in the future, specify the range of non-walkable tiles
	return(type > 1); //HACK: assumes >1 not walkable
}

SRect Map::GetBoundingBox(int x, int y) const
{
	SRect boundingBox;

	boundingBox.min.x = (float)x * mTileSize; 
	boundingBox.min.y = (float)y * mTileSize;
	boundingBox.max.x = (float)((x+1) * mTileSize) - 1; 
	boundingBox.max.y = (float)((y+1) * mTileSize) - 1; //-1 for pixel perfect corners

	return boundingBox;
}